#include <dspSimulator/instruction/stb.h>

bool STB::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void STB::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> STB::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<STB> stb;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b01101) && checkBits<1>(data, 8, 0b0) && existStandardCondition(data)) {
//       STB(.unit) *+baseR[offsetR], dst
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->creg_z = extractBits<4>(data, 28);
            stb->y = data[7];
            if (stb->y == 0) {
                stb->funcUnit = FuncUnit::D1;
            } else {
                stb->funcUnit = FuncUnit::D2;
            }
            stb->mode = extractBits<4>(data, 9).to_ulong();
            stb->offsetR = extractBits<5>(data, 13).to_ulong();
            stb->cst = stb->offsetR;
            stb->baseR = extractBits<5>(data, 18).to_ulong();
            stb->src1 = extractBits<5>(data, 23).to_ulong();
            stb->s = data[1];
            stb->p = data[0];
            stb->instFormat = InstFormat::Kind1;
        } else if (checkBits<5>(data, 2, 0b01111) && existStandardCondition(data)) {
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->creg_z = extractBits<4>(data, 28);
            stb->y = data[7];
            stb->funcUnit = FuncUnit::D2;
            stb->s = data[1];
            stb->cst = extractBits<15>(data, 8).to_ulong();
            stb->dst = extractBits<5>(data, 23).to_ulong();
            stb->p = data[0];
            stb->instFormat = InstFormat::Kind2;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (dspInstInfo->getDSZ() == 0b000 && data[9] == 1 && data[3] == 0 && data[10] == 0 &&
            checkBits<2>(data, 1, 0b10)) {
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->isCompactInstruction = true;
            stb->compactInstKind = "Doff4";
            stb->cst = extractBits<3>(data, 13).to_ulong();
            stb->cst = extractBits<1>(data, 11).to_ulong() << 3;
            stb->src1 = stb->dst = extractBits<3>(data, 4).to_ulong();
            stb->s = data[12]; // s is equal to t
            stb->y = data[0];
            stb->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            if (stb->y == 0) {
                stb->funcUnit = FuncUnit::D1;
            } else {
                stb->funcUnit = FuncUnit::D2;
            }
            stb->mode = 0b0001;
            stb->PROT = dspInstInfo->getPROT();
            stb->BR = dspInstInfo->getBR();
            stb->DSZ = dspInstInfo->getDSZ();
            stb->SAT = dspInstInfo->getSAT();
            stb->RS = dspInstInfo->getRS();
            stb->p = dspInstInfo->getP();
            stb->fixUpRegOnCompactInstruction();
            stb->instFormat = InstFormat::Kind1;
        } else if (dspInstInfo->getDSZ() == 0b001 && data[9] == 1 && data[3] == 0 && checkBits<2>(data, 10, 0b01) &&
                   checkBits<2>(data, 1, 0b10)) {
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->isCompactInstruction = true;
            stb->compactInstKind = "Dind";
            stb->src1 = extractBits<3>(data, 13).to_ulong();
            stb->offsetR = extractBits<3>(data, 4).to_ulong();
            stb->s = data[12]; // s is equal to t
            stb->y = data[0];
            stb->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            if (stb->y == 0) {
                stb->funcUnit = FuncUnit::D1;
            } else {
                stb->funcUnit = FuncUnit::D2;
            }
            stb->mode = 0b0101;
            stb->PROT = dspInstInfo->getPROT();
            stb->BR = dspInstInfo->getBR();
            stb->DSZ = dspInstInfo->getDSZ();
            stb->SAT = dspInstInfo->getSAT();
            stb->RS = dspInstInfo->getRS();
            stb->p = dspInstInfo->getP();
            stb->fixUpRegOnCompactInstruction();
            stb->instFormat = InstFormat::Kind1;
        } else if (dspInstInfo->getDSZ() == 0b000 && data[9] == 1 && data[3] == 0 && checkBits<2>(data, 14, 0b00) &&
                   checkBits<2>(data, 10, 0b11) && checkBits<2>(data, 1, 0b10)) {
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->isCompactInstruction = true;
            stb->compactInstKind = "Dinc";
            stb->cst = extractBits<1>(data, 13).to_ulong() + 1;
            stb->src1 = extractBits<3>(data, 4).to_ulong();
            stb->s = data[12]; // s is equal to t
            stb->y = data[0];
            stb->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            if (stb->y == 0) {
                stb->funcUnit = FuncUnit::D1;
            } else {
                stb->funcUnit = FuncUnit::D2;
            }
            stb->mode = 0b1011;
            stb->PROT = dspInstInfo->getPROT();
            stb->BR = dspInstInfo->getBR();
            stb->DSZ = dspInstInfo->getDSZ();
            stb->SAT = dspInstInfo->getSAT();
            stb->RS = dspInstInfo->getRS();
            stb->p = dspInstInfo->getP();
            stb->fixUpRegOnCompactInstruction();
            stb->instFormat = InstFormat::Kind1;
        } else if (dspInstInfo->getDSZ() == 0b001 && data[9] == 1 && data[3] == 0 && checkBits<2>(data, 14, 0b00) &&
                   checkBits<2>(data, 10, 0b11) && checkBits<2>(data, 1, 0b10)) {
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->isCompactInstruction = true;
            stb->compactInstKind = "Dinc";
            stb->cst = extractBits<1>(data, 13).to_ulong() + 1;
            stb->src1 = extractBits<3>(data, 4).to_ulong();
            stb->s = data[12]; // s is equal to t
            stb->y = data[0];
            stb->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            if (stb->y == 0) {
                stb->funcUnit = FuncUnit::D1;
            } else {
                stb->funcUnit = FuncUnit::D2;
            }
            stb->mode = 0b1011;
            stb->PROT = dspInstInfo->getPROT();
            stb->BR = dspInstInfo->getBR();
            stb->DSZ = dspInstInfo->getDSZ();
            stb->SAT = dspInstInfo->getSAT();
            stb->RS = dspInstInfo->getRS();
            stb->p = dspInstInfo->getP();
            stb->fixUpRegOnCompactInstruction();
            stb->instFormat = InstFormat::Kind1;
        } else if (dspInstInfo->getDSZ() == 0b101 && data[9] == 1 && data[3] == 0 && checkBits<2>(data, 14, 0b00) &&
                   checkBits<2>(data, 10, 0b11) && checkBits<2>(data, 1, 0b10)) {
            stb = std::make_shared<STB>(dspInstInfo->getAddress(), baseCycle);
            stb->isCompactInstruction = true;
            stb->compactInstKind = "Dinc";
            stb->cst = extractBits<1>(data, 13).to_ulong() + 1;
            stb->src1 = extractBits<3>(data, 4).to_ulong();
            stb->s = data[12]; // s is equal to t
            stb->y = data[0];
            stb->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            if (stb->y == 0) {
                stb->funcUnit = FuncUnit::D1;
            } else {
                stb->funcUnit = FuncUnit::D2;
            }
            stb->mode = 0b1011;
            stb->PROT = dspInstInfo->getPROT();
            stb->BR = dspInstInfo->getBR();
            stb->DSZ = dspInstInfo->getDSZ();
            stb->SAT = dspInstInfo->getSAT();
            stb->RS = dspInstInfo->getRS();
            stb->p = dspInstInfo->getP();
            stb->fixUpRegOnCompactInstruction();
            stb->instFormat = InstFormat::Kind1;
        }
    }
    return stb;
}

void STB::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    if (executePhase ==ExecutePhase::E1) {
        auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
        if (instFormat == InstFormat::Kind1) {
            u32 *baseRReadPtr;
            u32 *baseRWritePtr;
            if (y == 0) {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
            } else {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
            }
            m_visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 0, dspMachine);
        } else if (instFormat == InstFormat::Kind2) {
            u32 *baseRPtr;
            if (y == 0) {
                baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B14")->getDataPtr();
            } else {
                baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B15")->getDataPtr();
            }
            m_visitMemAddr = *baseRPtr + (cst << 0);
        }
        m_data = *src1LoRegPtr;
    } else if (executePhase ==ExecutePhase:: E3) {
        targetMachine->getMMU()->getMemory()->writeNBits<8>(m_visitMemAddr, {m_data});
    }
}

std::shared_ptr<DspInstruction> STB::clone() {
    return std::make_shared<STB>(*this);
}

std::string STB::toString() const {
    std::string res;
    if (isCompactInstruction) {

    } else {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)];
    if (instFormat == InstFormat::Kind1) {
        res += " " + getSrc1Reg32Name() + ",*" + addressModeToString();
    } else if (instFormat == InstFormat::Kind2) {
        res += " " + getSrc1Reg32Name() + ",*+";
        if (y == 0)
            res += "B14[" + std::to_string(cst) + "]";
        else
            res += "B15[" + std::to_string(cst) + "]";
    }
    return res;
}